package com.minigame.card.util;

import com.minigame.card.enums.Direction;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;

import static cn.hutool.core.collection.CollUtil.countMap;
import static com.minigame.card.paper.Constant.REPEAT_MAX;
import static java.util.Collections.shuffle;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;

public class CardUtil {

    /**
     * 初始化
     * @param messList 洗牌后所有牌
     * @param host 庄家方位
     * @param maxPlayerCardsCount 最多玩家手中牌数
     * @param rest 某个方位是否休息
     * @param <T> 牌类型
     * @return 各玩家牌
     */
    public static <T extends Enum<T>> Map<Direction, List<T>> init(List<T> messList, Direction host, int maxPlayerCardsCount, Predicate<Direction> rest, Comparator<T> comparator) {
        int playerCount = 0;
        for (Direction direction : Direction.values()) {
            if (!rest.test(direction)) {
                playerCount ++;
            }
        }
        List<List<T>> playersCards = dispatchInTurn(messList, playerCount, maxPlayerCardsCount);
        return adjustDirection(playersCards, host, rest, comparator);
    }

    /**
     * 洗牌
     * @param cells 元素集合
     * @return 洗牌后乱序所有牌
     * @param <T> 牌枚举类型
     */
    public static <T extends Enum<T>> List<T> wash(T[] cells) {
        int length = cells.length;
        List<T> messList = new ArrayList<>(length * REPEAT_MAX);
        for (T cell : cells) {
            for (int i = 0; i < REPEAT_MAX; i++) {
                messList.add(cell);
            }
        }
        shuffle(messList);
        return messList;
    }

    /**
     * 发牌(轮流)
     * @param messList 洗牌后乱序牌, 发完后相应减少
     * @param playerCount 当前局玩家数量(注意:按顺序东家第一个)
     * @param maxPlayerCardsCount 当前局发牌后最多初始牌数量
     * @return 各个玩家的牌
     * @param <T> 牌类型
     */
    public static <T> List<List<T>> dispatchInTurn(List<T> messList, int playerCount, int maxPlayerCardsCount) {
        List<List<T>> playersCells = new ArrayList<>(playerCount);
        for (int i = 0; i < playerCount; i++) {
            playersCells.add(new ArrayList<>());
        }
        int i = 0;
        Iterator<T> it = messList.iterator();
        while (it.hasNext()) {
            int mod = i++ % playerCount;
            List<T> cells = playersCells.get(mod);
            if (mod == 0) {
                if (cells.size() >= maxPlayerCardsCount) {
                    break;
                }
            }
            cells.add(it.next());
            it.remove();
        }
        return playersCells;
    }

    /**
     * 摸打牌逆时针依次为东、北、西、南
     * @param playersCells 各玩家牌
     * @param host 庄家
     * @param rest 是否休息
     * @param <T> 牌类型
     * @return 各方位玩家牌
     */
    public static <T> Map<Direction, List<T>> adjustDirection(List<List<T>> playersCells, Direction host, Predicate<Direction> rest, Comparator<T> comparator) {
        Map<Direction, List<T>> playerCardsMap = new LinkedHashMap<>();
        Direction direction = null;
        for (int i = 0; i < playersCells.size(); i++) {
            if (i == 0) {
                direction = host;
            } else {
                direction = direction.anticlockwiseNext();
            }
            if (!rest.test(direction)) {
                playerCardsMap.put(direction, playersCells.get(i));
            }
        }
        for (List<T> value : playerCardsMap.values()) {
            value.sort(comparator);
        }
        return playerCardsMap;
    }

    /**
     * 对玩家牌统计计数
     * @param playerList 玩家牌列表
     * @param cellCount 元素总数
     * @param getIndex 获取元素索引的方法
     * @param <T> 牌类
     * @return 玩家牌统计计数数组(无则为0)
     */
    public static <T> int[] countPlayerCell(List<T> playerList, int cellCount, Function<T, Integer> getIndex) {
        Map<T, Integer> countMap = countMap(playerList);
        int[] counts = new int[cellCount];
        countMap.forEach((t, count) -> counts[getIndex.apply(t)] = count);
        return counts;
    }

    /**
     * 检查去掉将牌后是否全部为刻和元
     * @param counts 每张牌数量
     * @param joinCount 可连接的牌数量
     * @param firstOfThree 元中首张牌
     */
    public static boolean check(int[] counts, int joinCount, Predicate<Integer> firstOfThree) {
        //刻
        for (int i = 0; i < counts.length; i++) {
            if (counts[i] >= 3) {
                counts[i] -= 3;
                if (check(counts, joinCount, firstOfThree)) {
                    return true;
                }
                counts[i] += 3;
            }
        }
        //元(风除外)
        for (int i = 0; i < joinCount; i++) {
            if (firstOfThree.test(i) && counts[i] > 0 && counts[i + 1] > 0 && counts[i + 2] > 0 ) {
                counts[i] --;
                counts[i + 1] --;
                counts[i + 2] --;
                if (check(counts, joinCount, firstOfThree)) {
                    return true;
                }
                counts[i] ++;
                counts[i + 1] ++;
                counts[i + 2] ++;
            }
        }
        //去掉刻,元后没牌则可胡
        for (int count : counts) {
            if (count > 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 根据牌列表统计每张牌数量
     * @param cards 牌列表
     * @param cellCount 元素总数
     * @param getIndex 获取索引方法
     * @param <T> 牌类型
     * @return 每张牌数量统计[没有则为0]
     */
    public static <T> int[] countList(List<T> cards, int cellCount, Function<T, Integer> getIndex) {
        int[] counts = new int[cellCount];
        Map<Integer, Integer> countMap = countMap(cards.stream().map(getIndex).collect(toList()));
        for (int i = 0; i < counts.length; i++) {
            counts[i] = ofNullable(countMap.get(i)).orElse(0);
        }
        return counts;
    }
}
